<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
    <title>MyApp</title>
    <base href="/">
    <link href="css/bootstrap/bootstrap.min.css" rel="stylesheet" />
    <link href="css/typography.css" rel="stylesheet" />
    <link href="css/markdown.css" rel="stylesheet" />
    <link href="css/app.css" rel="stylesheet">
    <link href="css/main-layout.css" rel="stylesheet" />
    <link href="MyApp.Client.styles.css" rel="stylesheet" />
    <script src="_content/Blazor.Extensions.Logging/blazor.extensions.logging.js" defer></script>
</head>

<body>
    <div id="app">
        <!-- loading: render temp static app chrome to improve perceived performance -->
        <div id="app-loading" class="main-layout page">
            <div class="sidebar">
                <div class="top-row navbar navbar-dark">
                    <a class="navbar-brand ps-4" href="/">MyApp</a>
                    <button class="navbar-toggler"><span class="navbar-toggler-icon"></span></button>
                </div>
                <div class="collapse">
                    <ul class="nav flex-column"></ul>
                </div>
            </div>
            <div class="main">
                <div class="main-top-row px-4">
                    <ul class="nav nav-pills"></ul>
                    <a href="signin?return=docs/deploy" class="btn btn-outline-primary">Login</a>
                </div>
                <div class="content px-4">

<div class="prose lg:prose-xl min-vh-100 m-3" data-prerender="/docs/hosting">
    <div class="markdown-body">
        <h1 id="app-hosting-costs">App Hosting Costs</h1>
<a href="https://jamstack.org">
    <img src="/img/jamstack-icon.svg" style="width:3.5rem;height:3.5rem;float:left;margin:.5rem 1rem 0 0">
</a>
<p>The modern <a href="https://jamstack.org">jamstack.org</a> approach for developing websites is primarily concerned with adopting
the architecture yielding the best performance and superior UX by minimizing the time to first byte from serving
pre-built static assets from CDN edge caches.</p>
<h2 id="cheaper-hosting">Cheaper Hosting</h2>
<a href="https://jamstack.org">
    <img src="/img/emoji-money.svg" style="width:3.5rem;height:3.5rem;float:left;margin:.5rem 1rem 0 0">
</a>
<p>A consequence of designing your UI decoupled from your back-end server is that it also becomes considerably
cheaper to host as its static files can be hosted by any web server and is a task highly optimized by CDNs
who are able to provide generous free &amp; low cost hosting options.</p>
<h2 id="myapp.client"><a href="https://github.com/NetCoreTemplates/blazor-wasm/tree/main/MyApp.Client">/MyApp.Client</a></h2>
<p>This template takes advantage of its decoupled architecture and uses <a href="/docs/deploy">GitHub Actions to deploy</a>
a copy of its static UI generated assets and hosted on:</p>
<h3 id="github-pages-cdn">GitHub Pages CDN</h3>
<h3 id="blazor-wasm.jamstacks.net"><a href="https://blazor-wasm.jamstacks.net">blazor-wasm.jamstacks.net</a></h3>
<p>This is an optional deployment step which publishes a copy of your .NET App's <code>/wwwroot</code> folder to this templates
<a href="https://github.com/NetCoreTemplates/blazor-wasm/tree/gh-pages">gh-pages</a> branch where it's automatically served from
<a href="https://docs.github.com/en/pages/getting-started-with-github-pages/about-github-pages">GitHub Pages CDN</a> at <strong>no cost</strong>.</p>
<p>It's an optional but recommended optimization as it allows the initial download from your website to be served
directly from CDN edge caches.</p>
<h2 id="myapp"><a href="https://github.com/NetCoreTemplates/blazor-wasm/tree/main/MyApp">/MyApp</a></h2>
<p>The .NET 6 <code>/MyApp</code> backend server is required for this App's dynamic functions including the Hello API on the home page
and its <a href="https://docs.servicestack.net/auth">built-in Authentication</a>.</p>
<p>The C# project still contains the complete App and can be hosted independently with the entire App served
directly from its deployed ASP.NET Core server at:</p>
<h3 id="digital-ocean">Digital Ocean</h3>
<h3 id="blazor-wasm-api.jamstacks.net"><a href="https://blazor-wasm-api.jamstacks.net">blazor-wasm-api.jamstacks.net</a></h3>
<p>But when accessed from the CDN <a href="https://blazor-wasm.jamstacks.net">blazor-wasm.jamstacks.net</a> that contains a
copy of its static <code>/wwwroot</code> UI assets, only its back-end JSON APIs are used to power its dynamic features.</p>
<h2 id="total-cost">Total Cost</h2>
<a href="https://www.digitalocean.com/pricing">
    <img src="/img/digital-ocean.svg" style="width:6.5rem;height:6.5rem;float:left;margin:0 1rem 0 0">
</a>
<p>Since hosting on GitHub Pages CDN is free, the only cost is for hosting this App's .NET Server which is being hosted
from a basic <a href="https://www.digitalocean.com/pricing">$10 /mo</a> droplet which is currently hosting <strong>25</strong> .NET Docker
Apps and demos of <a href="https://servicestack.net/start">starting project templates</a> which works out to be just under <strong>$0.40 /mo</strong>!</p>
<h2 id="jamstack-benefits">Jamstack Benefits</h2>
<p>Jamstack is quickly becoming the preferred architecture for the development of modern web apps with
<a href="https://jamstack.org/why-jamstack/">benefits</a> that extend beyond performance to improved:</p>
<ul>
<li><strong>Security</strong> from a reduced attack surface from hosting read-only static resources and requiring fewer App Servers</li>
<li><strong>Scale</strong> with non-essential load removed from App Servers to CDN's architecture capable of incredible scale &amp; load capacity</li>
<li><strong>Maintainability</strong> resulting from reduced hosting complexity and the clean decoupling of UI and server logic</li>
<li><strong>Portability</strong> with your static UI assets being easily capable from being deployed and generically hosted from any CDN or web server</li>
<li><strong>Developer Experience</strong> with the major JavaScript frameworks at the forefront of amazing DX are embracing Jamstack in their dev model, libraries &amp; tooling</li>
</ul>
<p>Best of all the Jamstack approach fits perfectly with ServiceStack's recommended
<a href="https://docs.servicestack.net/api-first-development">API First Development</a> model which encourages development of
reusable message-based APIs where the same System APIs can be reused from all Web, Mobile &amp; Desktop Apps
from multiple HTTP, MQ or gRPC endpoints.</p>

    </div>
</div>
                </div>
            </div>
        </div>
    </div>

<script>

const SIDEBAR = `
    Home,home,/$
    Counter,plus,/counter
    Todos,clipboard,/todomvc
    Bookings CRUD,calendar,/bookings-crud
    Call Hello,transfer,/hello$
    Call HelloSecure,shield,/hello-secure
    Fetch data,list-rich,/fetchdata
    Admin,lock-locked,/admin
    Login,account-login,/signin
`
const TOP = `
    0.40 /mo,dollar,/docs/hosting
    Prerendering,loop-circular,/docs/prerender
    Deployments,cloud-upload,/docs/deploy
`

const path = location.pathname
const NAV = ({ label, cls, icon, route, exact }) => `<li class="nav-item${cls}">
    <a href="${route}" class="nav-link${(exact ? path==route : path.startsWith(route)) ? ' active' : ''}">
        <span class="oi oi-${icon}" aria-hidden="true"></span> ${label}
    </a></li>`
const renderNav = (csv,f) => csv.trim().split(/\r?\n/g).map(s => NAV(f.apply(null,s.split(',')))).join('')
const $1 = s => document.querySelector(s)

$1('#app-loading .sidebar .nav').innerHTML = renderNav(SIDEBAR, (label, icon, route) => ({
    label, cls: ` px-3${route == SIDEBAR[0].route ? ' pt-3' : ''}`,
    icon, route: route.replace(/\$$/, ''), exact: route.endsWith('$')
}))

$1('#app-loading .main-top-row .nav').innerHTML = renderNav(TOP, (label, icon, route) => ({
    label, cls: '', icon, route: route.replace(/\$$/, ''), exact: route.endsWith('$')
}))
    
$1('#app-loading .navbar-toggler').onclick = function () {
    this.parentElement.parentElement.firstElementChild.nextElementSibling.classList.toggle('collapse') }

const pagePath = path.endsWith('/') ? path.substring(0, path.length - 2) + '/index.html' : path
fetch(`/prerender${pagePath}`)
    .then(r => r.text())
    .then(html => {
        if (html.indexOf('<!DOCTYPE html>') >= 0) return // don't show CDN 404.html pages
        const pageBody = $1('#app-loading .content')
        if (pageBody) pageBody.innerHTML = `<i hidden data-prerender="${path}"></i>` + html
    })
    .catch(/* no prerendered content found for this path */)
</script>
<style>
/* show subtle visual cue for prerendered pages */
#app-loading .content {
    box-shadow: inset 0 4px 4px 0 rgb(0 0 0 / 0.05);
}
</style>
 
    <div id="blazor-error-ui">
        An unhandled error has occurred.
        <a href="" class="reload">Reload</a>
        <a class="dismiss">🗙</a>
    </div>
    <script src="_framework/blazor.webassembly.js"></script>

<script src="/js/highlight.min.js"></script>
<script>hljs.highlightAll()</script>

<script>
/* DOM functions used in Blazor Components */

/* Loading */
window.prerenderedPage = function () {
    const el = document.querySelector('#app-loading .content')
    return el && el.innerHTML || ''
}
/* ShellCommand */
window.copy = function(target) {
    let $el = document.createElement("input")
    let $lbl = target.parentElement.querySelector('label')

    $el.setAttribute("value", $lbl.innerText)
    document.body.appendChild($el)
    $el.select()
    document.execCommand("copy")
    document.body.removeChild($el);

    if (typeof window.getSelection == "function") {
        const range = document.createRange()
        range.selectNodeContents($lbl)
        window.getSelection()?.removeAllRanges()
        window.getSelection()?.addRange(range)
    }
}
</script>
</body>
</html>
